#pragma once
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

const std::string SEM_PATH = "/tmp";
const int SEM_PROJ_ID = 0x77;
const int defaultnum = 1;
const int defaultperm = 0666; 
#define GET_SEM (IPC_CREAT)
#define BUILD_SEM (IPC_CREAT | IPC_EXCL)

union semun {
    int val;                  // 用于 SETVAL 和 GETVAL
    struct semid_ds* buf;     // 用于 IPC_STAT 和 IPC_SET
    unsigned short* array;    // 用于 SETALL 和 GETALL
};

// Product类：表示最终构建的产品 -- 负责部分操作
class Sem
{
public:
    Sem(int semid)
    :_semid(semid)
    {
    }

    //--
    void P(int index)
    {
        PV(index,-1);
    }
    //++
    void V(int index)
    {
        PV(index,1);
    }

    void Delete()
    {
        if(_semid >= 0)
        {
            int n = semctl(_semid,0,IPC_RMID);
            if(n < 0)
            {
                std::cerr<<"semctl IPC_RMID errer " << std::endl;
            }
            std::cout<<"semctl IPC_RMID succeed " << std::endl;
        }
    }

     //释放信号量
    ~Sem()
    {  
    }
private:
    void PV(int index, int op)
    {
        struct sembuf sb;
        sb.sem_num = index;
        sb.sem_op = op;
        sb.sem_flg = SEM_UNDO;

        int n = semop(_semid,&sb,1);
        if(n < 0)
        {
            std::cerr<<"semop errer " << std::endl;
            return;
        }

       // std::cout<<"semop succeed " << std::endl;
    }
private:
    int _semid;
};

// Builder抽象类：定义构建产品的接口
class SemBuilder
{
public:
    virtual ~SemBuilder() = default;

    virtual void BuildKey() = 0;
    virtual void SetPermission(int perm) = 0;
    virtual void SetSemNum(int num) = 0;
    virtual void SetInitVal(const std::vector<int>& initVal) = 0;
    virtual void Build(int flag) = 0;
    virtual void InitSem() = 0;
    virtual std::shared_ptr<Sem> GetSem() = 0;
};

// ConcreteBuilder类：实现Builder接口，具体构建产品
class ConcreteSemBuilder : public SemBuilder
{
public:
    ConcreteSemBuilder()
    {}
    virtual void BuildKey() override
    {
        _key = ftok(SEM_PATH.c_str(),SEM_PROJ_ID);
        if(_key < 0)
        {
            std::cerr<<"ftok errer " << std::endl;
            return;
        }

        std::cout<<"ftok succeed " << std::endl;
    }
    virtual void SetPermission(int perm) override
    {
        _perm = perm;
    }

    virtual void SetSemNum(int num) override
    {
        _num = num;
    }
    virtual void SetInitVal(const std::vector<int>& initVal) override
    {
        _initVal = initVal;
    }

    virtual void Build(int flag) override
    {
        //创建信号量 _num 只有在信号量集不存在时才会生效
        _semid = semget(_key,_num,flag | _perm);
        if(_semid < 0)
        {
            std::cerr<<"semget errer " << std::endl;
            return;
        }
        std::cout<<"semget succeed :  " <<_semid <<std::endl;

       
        _sem = std::make_shared<Sem>(_semid);
    }

    //设置每个信号量的数
    virtual void InitSem() override
    {
        if(_num == _initVal.size() && !_initVal.empty()) 
        {
            for(int i = 0; i < _initVal.size(); ++i)
            {
                if(_InitSem(i,_initVal[i]))
                {
                    break;
                }
            }
        }
    }


    virtual std::shared_ptr<Sem> GetSem() override
    {
        return _sem;
    }

    ~ConcreteSemBuilder()
    { 
    }

private:

    //设置信号量的数
    bool _InitSem(int index, int val)
    {
        union semun sem_union;
        sem_union.val = val;
        int n = semctl(_semid,index,SETVAL,sem_union);
        if(n < 0)
        {
            std::cerr<<"semctl errer " << std::endl;
            return false;
        }
        std::cout<<"semctl succeed " << std::endl;
        return true;
    }

private:
    key_t _key;
    int _perm;
    int _num;
    int _semid;
    std::vector<int> _initVal;
    std::shared_ptr<Sem> _sem = nullptr;
};

// Director类：指导构建过程
class Director
{
public:
    Director(std::shared_ptr<SemBuilder> sb)
    :_sb(sb)
    {}
    void ConstructSem(int flag, int num = 1,const std::vector<int>& initVal = {1}, int perm = defaultperm)
    {
        _sb->BuildKey();
        _sb->SetPermission(perm);
        _sb->SetSemNum(num);
        _sb->SetInitVal(initVal);
        _sb->Build(flag);
        if(flag == BUILD_SEM)
        {
            _sb->InitSem();
        }  
    }
private:
    std::shared_ptr<SemBuilder> _sb;
};
